#pragma once

#include "utils.h"
extern "C" {
#include "lauxlib.h"
#include "lua.h"
#include "lualib.h"
}

using LuaPtr = lua_State *;

inline void lua_pushqword(LuaPtr lptr, uint64 val) {
  lua_pushnumber(lptr, *(double *)&val);
}

inline uint64 lua_toqword(LuaPtr lptr, int pos) {
  double ret = lua_tonumber(lptr, pos);
  return *(QWORD *)&ret;
}

#define CheckTopGE(lptr, n)                                                    \
  if (!checktopge(lptr, n, __FUNCTION__, __LINE__))                            \
  break
#define CheckTopLE(lptr, n)                                                    \
  if (!checktople(lptr, n, __FUNCTION__, __LINE__))                            \
  break
#define CheckTop(lptr, n)                                                      \
  if (!checktop(lptr, n, __FUNCTION__, __LINE__))                              \
  break
#define CheckInteger(lptr, n)                                                  \
  if (!checkinteger(lptr, n, __FUNCTION__, __LINE__))                          \
  break
#define CheckType(lptr, type, n)                                               \
  if (!checktype(lptr, type, n, __FUNCTION__, __LINE__))                       \
  break

// 检查参数数量是否大于等于 n
inline bool checktopge(LuaPtr lptr, int n, const char *func_name, int line) {
  int num = lua_gettop(lptr);
  if (num >= n) {
    return true;
  } else {
    dbgprintu8("codergege [%s][%03d] 参数数量错误\n", func_name, line);
    return false;
  }
}

// 检查参数数量是否小于等于 n
inline bool checktople(LuaPtr lptr, int n, const char *func_name, int line) {
  int num = lua_gettop(lptr);
  if (num <= n) {
    return true;
  } else {
    dbgprintu8("codergege [%s][%03d] 参数数量错误\n", func_name, line);
    return false;
  }
}

// 检查参数数量是否为 n
inline bool checktop(LuaPtr lptr, int n, const char *func_name, int line) {
  int num = lua_gettop(lptr);
  if (num == n) {
    return true;
  } else {
    dbgprintu8("codergege [%s][%03d] 参数数量错误\n", func_name, line);
    return false;
  }
}

inline bool checkinteger(LuaPtr lptr, int index, const char *func_name,
                         int line) {
  if (index <= 0 || index > lua_gettop(lptr)) {
    dbgprintu8("codergege [%s][%03d] 参数索引错误\n", func_name, line);
    return false;
  }
  if (lua_isinteger(lptr, index)) {
    return true;
  }
  dbgprintu8("codergege [%s][%03d] 参数类型错误，期望的是 integer 类型\n",
             func_name, line);
  return false;
}

// 检查其他 type: LUA_TNIL, LUA_TNUMBER, LUA_TBOOLEAN, LUA_TSTRING,
// LUA_TTABLE, LUA_TFUNCTION, LUA_TUSERDATA, LUA_TTHREAD, and
// LUA_TLIGHTUSERDATA.
inline bool checktype(LuaPtr lptr, int type, int index, const char *func_name,
                      int line) {
  if (index <= 0 || index > lua_gettop(lptr)) {
    dbgprintu8("codergege [%s][%03d] 参数索引错误\n", func_name, line);
    return false;
  }
  int actual_type = lua_type(lptr, index);
  // 整数和浮点数在 Lua 中都是 LUA_TNUMBER 类型，所以需要额外判断
  if (type == LUA_TNUMBER) {
    if (lua_isnumber(lptr, index)) {
      if (lua_isinteger(lptr, index)) {
        dbgprintu8("codergege [%s][%03d] 参数类型错误，期望的是 float "
                   "类型，但得到的是 integer 类型\n",
                   func_name, line);
        return false;
      } else {
        return true;
      }
    } else {
      // 如果期望的是数字，但实际不是数字
      dbgprintu8("codergege [%s][%03d] 参数类型错误，期望的是 number 类型\n",
                 func_name, line);
      return false;
    }
  } else {
    if (actual_type == type) {
      return true;
    } else {
      const char *expected = "unknown";
      switch (type) {
      case LUA_TSTRING:
        expected = "string";
        break;
      case LUA_TBOOLEAN:
        expected = "boolean";
        break;
      // 添加其他类型的检查...
      default:
        break;
      }
      dbgprintu8("codergege [%s][%03d] 参数类型错误，期望的是 %s 类型\n",
                 func_name, line, expected);
      return false;
    }
  }
}

template <typename V> void pushkv(LuaPtr lptr, const char *k, V v) {
  // 这是一个通用模板, 它本身并不实现任何功能.
  // 特化版本将提供具体的实现.
}

template <> inline void pushkv<uint64>(LuaPtr lptr, const char *k, uint64 v) {
  lua_pushstring(lptr, k);
#if LUA_VERSION_NUM >= 503
  lua_pushinteger(lptr, v);
#else
  // 对于小于 Lua 5.3 的版本
  pushqword(lptr, v);
#endif
  lua_settable(lptr, -3);
}

template <> inline void pushkv<uint32>(LuaPtr lptr, const char *k, uint32 v) {
  lua_pushstring(lptr, k);
  lua_pushinteger(lptr, v);
  lua_settable(lptr, -3);
}

template <> inline void pushkv<uint16>(LuaPtr lptr, const char *k, uint16 v) {
  lua_pushstring(lptr, k);
  lua_pushinteger(lptr, v);
  lua_settable(lptr, -3);
}

template <> inline void pushkv<uint8>(LuaPtr lptr, const char *k, uint8 v) {
  lua_pushstring(lptr, k);
  lua_pushinteger(lptr, v);
  lua_settable(lptr, -3);
}

template <> inline void pushkv<bool>(LuaPtr lptr, const char *k, bool v) {
  lua_pushstring(lptr, k);
  lua_pushboolean(lptr, v);
  lua_settable(lptr, -3);
}

template <> inline void pushkv<float>(LuaPtr lptr, const char *k, float v) {
  lua_pushstring(lptr, k);
  lua_pushnumber(lptr, v);
  lua_settable(lptr, -3);
}

template <> inline void pushkv<double>(LuaPtr lptr, const char *k, double v) {
  lua_pushstring(lptr, k);
  lua_pushnumber(lptr, v);
  lua_settable(lptr, -3);
}

template <>
inline void pushkv<const char *>(LuaPtr lptr, const char *k, const char *v) {
  lua_pushstring(lptr, k);
  lua_pushstring(lptr, v);
  lua_settable(lptr, -3);
}

template <>
inline void pushkv<std::string>(LuaPtr lptr, const char *k, std::string v) {
  lua_pushstring(lptr, k);
  lua_pushstring(lptr, v.c_str());
  lua_settable(lptr, -3);
}

inline void push_to_lua(LuaPtr lptr, bool val) { lua_pushboolean(lptr, val); }
inline void push_to_lua(LuaPtr lptr, uint8 val) { lua_pushinteger(lptr, val); }
inline void push_to_lua(LuaPtr lptr, uint64 val) { lua_pushinteger(lptr, val); }
inline void push_to_lua(LuaPtr lptr, uint32 val) { lua_pushinteger(lptr, val); }
inline void push_to_lua(LuaPtr lptr, float val) { lua_pushnumber(lptr, val); }
inline void push_to_lua(LuaPtr lptr, const char *val) {
  lua_pushstring(lptr, val);
}
// ... 其他类型重载

// 辅助函数：将 C++ lambda 包装进 Lua Userdata 并绑定为函数
// 无参数 比如 player.x()
template <typename F> void pushkf(LuaPtr lptr, const char *key, F func) {
  lua_pushstring(lptr, key);

  // 1. 在 Lua 堆上分配一个完整的 Userdata 来存储 Lambda
  // 这个 Userdata 的生命周期由 Lua 管理
  F *func_ptr = static_cast<F *>(lua_newuserdata(lptr, sizeof(F)));
  new (func_ptr) F(func); // 使用 placement new 构造 lambda

  // 2. 创建一个元表用于垃圾回收 (__gc)
  lua_newtable(lptr);
  lua_pushstring(lptr, "__gc");
  lua_pushcfunction(lptr, [](LuaPtr l) -> int {
    // 当 Lua GC 回收时，执行析构并释放内存
    F *f = static_cast<F *>(lua_touserdata(l, 1));
    f->~F(); // 手动调用析构函数
    return 0;
  });
  lua_settable(lptr, -3);     // metatable.__gc = gc_func
  lua_setmetatable(lptr, -2); // 将元表设置给 Userdata

  // 3. 将 Userdata 包装成一个可调用的 C 闭包
  lua_pushcclosure(
      lptr,
      [](LuaPtr l) -> int {
        // C 闭包执行时，从 upvalue[1] 获取 lambda Userdata
        F *f = static_cast<F *>(lua_touserdata(l, lua_upvalueindex(1)));

        // 调用捕获的 lambda，并将结果推送到 Lua 栈
        // 这需要一个重载辅助函数来处理不同返回类型
        // 这里假设 lambda 返回一个能被 push_to_lua 处理的类型
        // f 是一个函数对象，调用它 (*f)()
        push_to_lua(l, (*f)()); // 需要实现 push_to_lua
        return 1;               // 返回一个值
      },
      1); // 1 表示有一个 upvalue (userdata)

  lua_settable(lptr, -3); // 将闭包作为值设置到 player 表格的 key 上
}

// 辅助函数：将 C++ lambda 包装进 Lua Userdata 并绑定为函数
// 1个string 参数 比如 player.hasBuff("王者祝福")
template <typename F> void pushkf_str(LuaPtr lptr, const char *key, F func) {
  lua_pushstring(lptr, key);

  // 1. 在 Lua 堆上分配一个完整的 Userdata 来存储 Lambda
  // 这个 Userdata 的生命周期由 Lua 管理
  F *func_ptr = static_cast<F *>(lua_newuserdata(lptr, sizeof(F)));
  new (func_ptr) F(func); // 使用 placement new 构造 lambda

  // 2. 创建一个元表用于垃圾回收 (__gc)
  lua_newtable(lptr);
  lua_pushstring(lptr, "__gc");
  lua_pushcfunction(lptr, [](LuaPtr l) -> int {
    // 当 Lua GC 回收时，执行析构并释放内存
    F *f = static_cast<F *>(lua_touserdata(l, 1));
    f->~F(); // 手动调用析构函数
    return 0;
  });
  lua_settable(lptr, -3);     // metatable.__gc = gc_func
  lua_setmetatable(lptr, -2); // 将元表设置给 Userdata

  // 3. 将 Userdata 包装成一个可调用的 C 闭包
  lua_pushcclosure(
      lptr,
      [](LuaPtr l) -> int {
        // C 闭包执行时，从 upvalue[1] 获取 lambda Userdata
        F *f = static_cast<F *>(lua_touserdata(l, lua_upvalueindex(1)));

        // 检查参数数量和类型
        if (lua_gettop(l) != 1) {
          luaL_error(l, "hasBuff expected exactly 1 argument.");
          return 0;
        }
        if (!lua_isstring(l, 1)) {
          luaL_error(l, "hasBuff expected string argument.");
          return 0;
        }

        // 从 Lua 栈读取第一个参数
        std::string buffName = lua_tostring(l, 1);

        // 调用 C++ lambda 函数，传入从 Lua 获取的参数
        // 假设 F 返回 bool
        bool result = (*f)(buffName);

        // 将结果推送到 Lua 栈
        push_to_lua(l, result);
        return 1; // 返回一个值
      },
      1);                 // 1 表示有一个 upvalue (userdata)
  lua_settable(lptr, -3); // 将闭包作为值设置到 player 表格的 key 上
}
